package com.faselbaum.stalkme.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import com.faselbaum.stalkme.models.BluetoothDeviceInfo;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.widget.ArrayAdapter;
import android.widget.ListView;

// Provides easy Bluetooth Access
public class BluetoothAccess
{
	private Activity _activity = null;
	private BluetoothAdapter _btAdapter = BluetoothAdapter.getDefaultAdapter();
	private IntentFilter _btListener = null;
	
	private HashMap<String,BluetoothDeviceInfo> _deviceList = new HashMap<String,BluetoothDeviceInfo>();
	
	private int _genericITL = 0;
	
	private Handler _onUpdateHandler = null;
	private Runnable _onUpdateRunnable = null;
	
	private Timer _scanTimer = new Timer();
	//The task that will get executed by the scanTimer
	//handling the death of a device when its ITL have run out
	//recalculating the relative RSSIs of all devices
	private TimerTask _scanTask = new TimerTask()
	{
		
		@Override
		public void run()
		{
			// TODO Auto-generated method stub
			ArrayList<String> toRemove = new ArrayList<String>();
			for(BluetoothDeviceInfo device : _deviceList.values())
			{
				device.setITL(device.getITL()-1);
				if(device.getITL()<1)
				{
					toRemove.add(device.getMACAddress());
				}
			}
			for(String macAddress : toRemove)
			{
				_deviceList.remove(macAddress);
			}
			recalculateRelativeRSSIs();
			notifyUpdate();
			startBroadcast();
		}
	};
	
	//Constructor
	//activity is needed for setting up a new Broadcastreceiver and unregister it on cleanup
	public BluetoothAccess(Activity activity)
	{
		_activity = activity;
		_scanTimer = new Timer();
	}
	
	//Sets the interval, to scan for new Bluetooth Devices and gets the scanTimer started
	public void setScanInterval(long milliseconds)
	{
		_scanTimer.cancel();
		_scanTimer = new Timer();
		_scanTimer.schedule(_scanTask,0,milliseconds);
	}
	
	//stops the scanTimer
	public void stopScanning()
	{
		_scanTimer.cancel();
		_scanTimer.purge();
		stopBroadcast();
	}
	
	//recalculates the relative RSSI for every device in the list
	private void recalculateRelativeRSSIs()
	{
		short maxRSSI = Short.MIN_VALUE;
		double relativeValue;
		
		for(BluetoothDeviceInfo d : _deviceList.values())
		{
			if(d.getRSSI()>maxRSSI)
			{
				maxRSSI = d.getRSSI(); 
			}
		}
		
		maxRSSI+=100;
		
		for(BluetoothDeviceInfo device : _deviceList.values())
		{
			relativeValue = 1;
			relativeValue = (double)(100+device.getRSSI())/(double)maxRSSI;
			device.setRelativeRSSI(relativeValue);
		}
		
	}
	
	//Broadcasting mechanism for detecting Bluetooth devices
	private BroadcastReceiver _broadCastReceiver = new BroadcastReceiver()
	{
		@Override
		public void onReceive(Context context, Intent intent)
		{
			String action = intent.getAction();
			if(action.equals(BluetoothDevice.ACTION_FOUND))
			{
				short signalStrength = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				String macAddress = device.getAddress();
				String name = device.getName();
				
				if(!_deviceList.containsKey(macAddress))
				{
					_deviceList.put(macAddress, new BluetoothDeviceInfo(macAddress,name,signalStrength,_genericITL));
				}
				else
				{
					_deviceList.get(macAddress).setName(name);
					_deviceList.get(macAddress).setRSSI(signalStrength);
					_deviceList.get(macAddress).setITL(_genericITL);
				}
				recalculateRelativeRSSIs();
				notifyUpdate();
			}
		}
	};
	
	//Starts a broadcast
	//Condition: there's currently no active broadcast running
	private void startBroadcast()
	{
		if(_activity!=null && _btAdapter!=null)
		{
			if(!_btAdapter.isDiscovering())
			{
				_btListener = new IntentFilter(BluetoothDevice.ACTION_FOUND);
				_activity.registerReceiver(_broadCastReceiver,_btListener);
				_btAdapter.startDiscovery();				
			}
		}
	}
	
	//stops the running broadcast
	private void stopBroadcast()
	{
		if(_activity!=null && _btAdapter!=null)
		{
			if(_btAdapter.isDiscovering()) 
			{
				_btAdapter.cancelDiscovery();	
			}
			_activity.unregisterReceiver(_broadCastReceiver);			
		}
	}
	
	//returns the MAC-Address of the local bluetooth adapter
	public String getMACAddress()
	{
		if(_btAdapter==null) return "";
		else return _btAdapter.getAddress();
	}
	
	// returns the visible bluetooth name of the phone
	public String getDeviceName()
	{
		if(_btAdapter==null) return "";
		else return _btAdapter.getName();
	}
	
	// returns the BluetoothDeviceInfo for the local adapter
	public BluetoothDeviceInfo getLocalDeviceInfo()
	{
		BluetoothDeviceInfo info = new BluetoothDeviceInfo();
		info.setMACAddress(getMACAddress());
		info.setName(getDeviceName());
		return info;
	}
	
	//indicates whether the local bluetooth adapter is enabled or not
	public boolean isAdapterEnabled()
	{
		if(_btAdapter==null) return false;
		return _btAdapter.isEnabled();
	}
	
	//enables or disables the local bluetooth adapter
	public boolean setAdapterEnabled(boolean value)
	{
		if(_btAdapter!=null)
		{
			if(value) return _btAdapter.enable();
			else return _btAdapter.disable();
		}
		return false;
	}
	
	//returns all recently scanned devices in an ArrayList
	public ArrayList<BluetoothDeviceInfo> getNearbyDevices()
	{
		return (ArrayList<BluetoothDeviceInfo>) _deviceList.values();
	}
	
	//returns a hashmap of the currently held bluetoothdevices (key = macAddress as String, value = BluetoothDeviceInfo)
	public HashMap<String, BluetoothDeviceInfo> getNearbyDevicesMap()
	{
		return _deviceList;
	}
	
	//returns the number of iterations a bluetooth device will live since the last FOUND was received 
	public int getGenericITL()
	{
		return _genericITL;
	}
	
	//sets the number of iterations a bluetooth device will live since the last FOUND was received
	public void setGenericITL(int itl)
	{
		_genericITL = itl;
	}
	
	public void setOnUpdateRunnable(Runnable runnable)
	{
		if(_onUpdateHandler==null) 
		{
			_onUpdateHandler = new Handler();
		}
		else 
		{
			if(_onUpdateRunnable!=null) _onUpdateHandler.removeCallbacks(_onUpdateRunnable);
		}
		_onUpdateRunnable = runnable;
	}
	
	public void clearOnUpdateRunnable()
	{
		if(_onUpdateHandler!=null) 
		{
			if(_onUpdateRunnable!=null)
			{
				_onUpdateHandler.removeCallbacks(_onUpdateRunnable);				
			}
		}
	}
	
	private void notifyUpdate()
	{
		if(_onUpdateHandler!=null && _onUpdateRunnable!=null)
		{
			_onUpdateHandler.removeCallbacks(_onUpdateRunnable);
			_onUpdateHandler.post(_onUpdateRunnable);
		}
	}
	
}
